package com.jinke.api.modules.third.uniuni.tool;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.github.rholder.retry.RetryException;
import com.github.rholder.retry.Retryer;
import com.jinke.api.common.annotation.Apilog;
import com.jinke.api.common.exception.Asserts;
import com.jinke.api.common.retry.NeedRetryException;
import com.jinke.api.common.retry.UniRetryBuilder;
import com.jinke.api.modules.base.constant.EnvironmentConstants;
import com.jinke.api.modules.base.constant.RedisKeyConstants;
import com.jinke.api.modules.third.ezeeship.model.request.EstimateRateRequest;
import com.jinke.api.modules.third.uniuni.model.constants.UniUniApiConstants;
import com.jinke.api.modules.third.uniuni.model.dto.*;
import com.jinke.api.modules.third.uniuni.model.response.UniResponse;
import com.jinke.api.modules.third.uniuni.model.response.UniTokenResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Optional;
import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * @projectName:
 * @title: UniUniRequest
 * @description: uniuni请求工具
 * @see:
 * @author: hhp
 * @version: 1.0.0
 * @createTime: 2024/8/7 10:48
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class UniUniRequest {

    @Value("${uni-uni.client-id}")
    private String clientId;
    @Value("${uni-uni.customer-id}")
    private String customerId;
    @Value("${uni-uni.client-secret}")
    private String clientSecret;
    // 沙盒环境请求网址
    @Value("${uni-uni.sandbox-url}")
    private String sandboxUrl;
    @Value("${uni-uni.qa-url}")
    private String qaUrl;
    // 生产环境请求网址
    @Value("${uni-uni.prod-url}")
    private String prodUrl;
    @Value("${uni-uni.tracking-key}")
    private String trackingKey;
    private final Environment environment;
    private final UniRetryBuilder uniRetryBuilder;
    private final StringRedisTemplate stringRedisTemplate;

    public UniResponse.Data getFullToken() {
        UniResponse.Data tokenResponse = null;
        String sendUrl = getApiPrefix() + UniUniApiConstants.CUSTOMER_TOKEN;
        String uniTokenReqStr = JSONObject.toJSONString(new UniTokenReqDto()
                .setGrantType(UniUniApiConstants.GRANT_TYPE)
                .setClientId(clientId)
                .setClientSecret(clientSecret));
        String responseData = sendPostRequest(sendUrl, uniTokenReqStr);
        if (StrUtil.isNotBlank(responseData)) {
            UniResponse uniResponse = JSONObject.parseObject(responseData, UniResponse.class);
                // 请求成功
            tokenResponse = uniResponse.getData();
        }
        log.info("uniuni获取token响应:{}", tokenResponse);
        return tokenResponse;
    }


    /**
     * @Author: hhp
     * @Description: uni创建货件
     * @Data: 2024/8/8 10:26
     * @return: com.jinke.api.modules.third.uniuni.model.response.UniResponse<com.jinke.api.modules.third.uniuni.model.response.CreateOrderResponse>
     * @throws:
     **/
    @Apilog(requestTypeName = "uniuni-下单")
    public String createLabel(EstimateRateRequest request) {
        UniCreateBusOrderDto uniCreateBusOrderDto = formatParam(request);
        String sendUrl = getApiPrefix() + UniUniApiConstants.CREATE_BUSINESS_ORDER;
        String param = JSONObject.toJSONString(uniCreateBusOrderDto);
        String orderNo = request.getOrderNO();
        log.info("===========uniuni下单请求======{},{}", orderNo, param);
        String response = sendPostRequest(sendUrl, param);
        //String response="{\"status\":\"SUCCESS\",\"ret_msg\":\"\",\"err_code\":0,\"data\":{\"tno\":\"UUS48C1153312113397\",\"order_id\":31211339,\"uni_order_sn\":\"2024081231211339\",\"uni_order_status\":\"ORDER_RECEIVED\",\"uni_status_code\":190,\"sorting\":1,\"source\":\"s\",\"order\":{\"start_consignee\":\"nina\",\"partner_shop_name\":\"Ship Cheap Inc\",\"partner_shop_id\":327,\"start_email\":\"sales@ship-cheap.com\",\"start_mobile\":\"57489385769\",\"start_address\":\"1134 Western Ave\",\"start_zipcode\":\"79905\",\"start_area\":0,\"start_sign_building\":\"\",\"start_lng\":-73.8205651,\"start_lat\":42.6753408,\"consignee\":\"test\",\"address\":\"1134 Western Ave\",\"zipcode\":\"91201\",\"end_area\":0,\"mobile\":\"37589409879\",\"phone_extra\":\"\",\"email\":\"\",\"sign_building\":\"\",\"transport_type\":0,\"buzz_code\":\"\",\"lng\":\"-118.288307\",\"lat\":\"34.171963\",\"postscript\":\"\",\"driver_notes\":\"Suspected Apt\\/Suspect\\u00e9 Apt\",\"task_type\":0,\"task_time\":\"\",\"task_speed\":2,\"extra_order_sn\":\"UUS48C1153312113397\",\"referer\":\"L202408120115\",\"sub_referer\":\"N\\/A\",\"inv_type\":\"NORMAL\",\"warehouse\":1,\"type\":2,\"shipping_staff_id\":100151,\"receive_time_label\":\"08-12 6:00AM - 9:00AM\",\"fee\":0,\"pay_fee\":0,\"fee_per\":0,\"need_insure\":0,\"goods_value\":0,\"insure_fee\":0,\"need_box\":0,\"need_call\":0,\"distance\":0,\"shipping_fee\":6,\"tax\":0,\"pay_status\":0,\"pay_id\":8,\"pay_name\":\"EMT\",\"is_main_order\":0,\"order_amount\":0,\"user_id\":1,\"add_time\":1723440146,\"add_order_time\":\"2024-08-12 05:22:26\",\"shop_id\":5468,\"partner_id\":115,\"address_has_issue\":0,\"is_accepted\":1,\"carrier_id\":1,\"shop_lat\":42.6753408,\"shop_lng\":-73.8205651,\"shipping_name\":\"\",\"customer_id\":\"327\",\"main_order_id\":0,\"order_sn\":\"2024081231211339\",\"order_id\":31211339},\"tracking\":{\"order_id\":31211339,\"order_sn\":\"2024081231211339\",\"pathAddr\":\"UNI DATA CENTER\",\"pathInfo\":\"ORDER_RECEIVED\",\"state\":190,\"rcountry\":\"CA\",\"tno\":\"UUS48C1153312113397\",\"is_addon\":0,\"second_delivery_sn\":\"\",\"consignee\":\"test\",\"address1\":\"1134 Western Ave\",\"shipper_country_code\":\"US\",\"city\":\"Glendale\",\"province\":\"CA\",\"province_code\":\"CA\",\"country_code\":\"US\",\"zip\":\"91201\",\"warehouse\":1,\"segment\":\"LAX-Zone-D\",\"owner\":\"LAX\",\"grid_code\":\"\",\"email\":\"\",\"phone\":\"37589409879\",\"reference\":\"L202408120115\",\"sub_reference\":\"N\\/A\",\"bag_no\":\"\",\"goods_type\":0,\"express_type\":0,\"danger_type\":0,\"weight_type\":2,\"size_type\":1,\"dispatch_type\":\"{\\\"SZ\\\":2,\\\"SG\\\":0,\\\"DT\\\":0,\\\"SP\\\":0}\",\"item_description\":\"\",\"require_signature\":0,\"length\":11,\"width\":11,\"height\":5,\"total_weight\":22.42,\"weight_uom\":\"LBS\",\"dimension_uom\":\"IN\",\"internal_account_number\":\"20240812d79186\",\"total_value\":null,\"currency\":\"CAD\",\"update_time\":1723440146,\"address_type\":1,\"shipper\":null,\"need_transshippment\":false},\"spath\":{\"order_id\":31211339,\"code\":190,\"pathAddr\":\"UNI DATA CENTER\",\"pathTime\":1723440146,\"pathInfo\":\"ORDER_RECEIVED\",\"traceSeq\":0}}}";
        log.info("===========uniuni下单响应======{},{}", orderNo, response);
        return response;
    }

    /**
     *
     * @param request
     * @return
     */
    private UniCreateBusOrderDto formatParam(EstimateRateRequest request) {
        UniCreateBusOrderDto uniCreateBusOrderDto=new UniCreateBusOrderDto();
        uniCreateBusOrderDto.setCustomerNo(customerId);
        //货件的批号
        uniCreateBusOrderDto.setReference(request.getBatchNO()) ;
        //外部号
        uniCreateBusOrderDto.setInternalAccountNumber(request.getObjectId()) ;
        uniCreateBusOrderDto.setSender(request.getFrom().getPersonName()) ;
        uniCreateBusOrderDto.setStartPhone(request.getFrom().getPhone()) ;
        uniCreateBusOrderDto.setStartPostalCode(request.getFrom().getZipCode()) ;
        //Uniuni 接收货物的位置的完整地址。它用于计算运费，作为包裹的起点。 请按照“市民号码+街道、城市、省/州、国家、邮政编码、#单位号码”的顺序操作。
        uniCreateBusOrderDto.setPickupAddress(request.getTo().getAddressLine1()+request.getTo().getAddressLine2()) ;
        uniCreateBusOrderDto.setShipperCountryCode(request.getFrom().getCountryCode()) ;
        uniCreateBusOrderDto.setReceiver(request.getTo().getPersonName()) ;
        uniCreateBusOrderDto.setCity(request.getTo().getCity()) ;
        uniCreateBusOrderDto.setProvince(request.getTo().getStateCode()) ;
        //两个字母缩写的
        uniCreateBusOrderDto.setCountry(request.getTo().getCountryCode()) ;
        //是否是住宅
        Boolean isResidential = request.getTo().getIsResidential();
        uniCreateBusOrderDto.setAddressType(isResidential?1:0) ;
        uniCreateBusOrderDto.setPostalCode(request.getTo().getZipCode()) ;
        //发
        uniCreateBusOrderDto.setDeliveryAddress(request.getFrom().getAddressLine1()+request.getFrom().getAddressLine2()) ;
        uniCreateBusOrderDto.setReceiverPhone(request.getTo().getPhone()) ;
        uniCreateBusOrderDto.setReceiverEmail(request.getTo().getEmail()) ;
        //长宽高
        List<EstimateRateRequest.Parcel> parcels = request.getParcels();
        EstimateRateRequest.Parcel parcel = parcels.get(0);
        uniCreateBusOrderDto.setLength(new BigDecimal(parcel.getLength())) ;
        uniCreateBusOrderDto.setWidth(new BigDecimal(parcel.getWidth())) ;
        uniCreateBusOrderDto.setHeight(new BigDecimal(parcel.getHeight())) ;
        uniCreateBusOrderDto.setDimensionUom(parcel.getDistanceUnit()) ;
        uniCreateBusOrderDto.setWeight(new BigDecimal(parcel.getWeight())) ;
        //KGS、LBS 或 OZS。
        if (parcel.getMassUnit().equals("lb")){
            uniCreateBusOrderDto.setWeightUom("LBS") ;
        }else {
            uniCreateBusOrderDto.setWeightUom("KGS") ;
        }
        //项目描述
        //uniCreateBusOrderDto.setItemDescription(request.getBatchNO()) ;
        //自定义消息
        uniCreateBusOrderDto.setCustomField(request.getExtra().getReference()+request.getExtra().getReference2()) ;
        return  uniCreateBusOrderDto;

    }

    /**
     * 预估装运运费
     * @return
     */
    @Apilog(requestTypeName = "uniuni-预估")
    public String estimate(EstimateRateRequest estimateRateRequest) {
        String sendUrl = getApiPrefix() + UniUniApiConstants.ESTIMATESHIPPING;
        UniEstimateReqDto dto = getUniEstimateReqDto(estimateRateRequest);
        String param = JSONUtil.toJsonStr(dto);
        //从参数带进来
        String orderNo = estimateRateRequest.getOrderNO();
        log.info("===========uniuni预估请求======  {}, {}", orderNo, param);
        String response = this.sendPostRequest(sendUrl, param);
        //{"status":"SUCCESS","ret_msg":"","err_code":0,"data":{"customer_id":0,"start_postal_code":"54182","postal_code":"92821","length":"3","width":"3","height":"3","weight":"3","weight_uom":"LBS","carrier_id":1,"shippingCharge":6,"useDimensionWeight":false,"fuelSurcharge":0,"totalBeforeTax":6,"tax":0,"totalAfterTax":6}}
        log.info("===========uniuni预估响应======{},{}", orderNo, response);
        return response;

    }

    /**
     * 组装预估参数
     * @param estimateRateRequest
     * @return
     */
    @NotNull
    private UniEstimateReqDto getUniEstimateReqDto(EstimateRateRequest estimateRateRequest) {
        //把统一的预估和下单对象转成uni要的参数实体
        EstimateRateRequest.Parcel parcel = estimateRateRequest.getParcels().get(0);

        UniEstimateReqDto dto = new UniEstimateReqDto();
        dto.setStart_postal_code(estimateRateRequest.getFrom().getZipCode());
        dto.setPostal_code(estimateRateRequest.getTo().getZipCode());
        dto.setLength(parcel.getLength());
        dto.setWidth(parcel.getWidth());
        dto.setHeight(parcel.getHeight());
        dto.setWeight(parcel.getWeight());

        //KGS、LBS 或 OZS。
        if (parcel.getMassUnit().equals("lb")){
            dto.setWeight_uom("LBS") ;
        }else {
            dto.setWeight_uom("KGS") ;
        }

        return dto;
    }

    public String sendPostRequest(String sendUrl, String param) {
        OkHttpClient client = new OkHttpClient().newBuilder()
                .build();
        MediaType mediaType = MediaType.parse("application/json");
        RequestBody body = RequestBody.create(mediaType, param);
        Request.Builder builder = new Request.Builder()
                .url(sendUrl)
                .addHeader("Content-Type", "application/json")
                .method("POST", body);
        if (!Optional.of(sendUrl).orElse("").contains(UniUniApiConstants.CUSTOMER_TOKEN)) {
            // 只有获取token不加请求头
            String uniRedisToken = getUniRedisToken();
            builder.header("Authorization", uniRedisToken);
        }
        Request request = builder.build();
        return getResponse(client, request);
    }

    public String sendGetRequest(String sendUrl) {
        OkHttpClient client = new OkHttpClient().newBuilder()
                .build();
        Request.Builder builder = new Request.Builder()
                .url(sendUrl);
        if (!Optional.of(sendUrl).orElse("").contains(UniUniApiConstants.CUSTOMER_TOKEN)) {
            // 只有获取token不加请求头
            String uniRedisToken = getUniRedisToken();
            builder.header("Authorization", uniRedisToken);
        }
        Request request = builder.build();
        return getResponse(client, request);
    }

    /**
     * @Author: hhp
     * @Description: uniuni通过orderNo取消发货
     * @Data: 2024/8/8 16:13
     * @param uniDelOrderIANReqDTO
     * @param orderNo
     * @return: java.lang.String
     * @throws:
     **/
    @Apilog(requestTypeName = "uniuni-o-取消")
    public String deleteOrdersByIAN(UniDelOrderIANReqDTO uniDelOrderIANReqDTO, String orderNo) {
        String sendUrl = getApiPrefix() + UniUniApiConstants.DELETE_ORDERS_BY_IAN;
        String param = JSONObject.toJSONString(uniDelOrderIANReqDTO);
        log.info("============uniuni通过objectId取消请求=============={},{}", orderNo, param);
        String responseData = sendPostRequest(sendUrl, param);
        log.info("============uniuni通过objectId取消响应=============={},{}", orderNo,  responseData);
        return responseData;
    }

    /**
     * @Author: hhp
     * @Description: 通过追踪号码取消发货
     * @Data: 2024/8/8 16:42
     * @param uniCancelOrderReqDTO
     * @param orderNo
     * @return: java.lang.String
     * @throws:
     **/
    @Apilog(requestTypeName = "uniuni-t-取消")
    public String cancelOrder(UniCancelOrderReqDTO uniCancelOrderReqDTO, String orderNo) {
        String sendUrl = getApiPrefix() + UniUniApiConstants.CANCEL_ORDER;
        String param = JSONObject.toJSONString(uniCancelOrderReqDTO);
        log.info("============uniuni通过trackingnumber取消请求==========={},{}", orderNo, param);
        String responseData = sendPostRequest(sendUrl, param);
        log.info("============uniuni通过trackingnumber取消响应==========={},{}", orderNo, responseData);
        return responseData;
    }

    public String printLabel(UniPrintLabelReqDTO uniPrintLabelReqDTO) {
        String sendUrl = getApiPrefix() + UniUniApiConstants.PRINT_LABEL;
        String param = JSONObject.toJSONString(uniPrintLabelReqDTO);
        String tno = uniPrintLabelReqDTO.getPackageId();
        log.info("============uniuni打印面单请求==========={},{}", tno, param);
        String responseData = sendPostRequest(sendUrl, param);
        log.info("============uniuni打印面单响应==========={},{}", tno, responseData);
        return responseData;
    }

    public String trackingUni(String trackingNum) {
        StringBuilder sendUrl = new StringBuilder();
        sendUrl.append(getApiPrefix());
        sendUrl.append(UniUniApiConstants.PRINT_LABEL);
        sendUrl.append("?key=");
        sendUrl.append(trackingKey);
        sendUrl.append("&id=");
        sendUrl.append(trackingNum);
        log.info("==========uniuni通过跟踪号跟踪货物参数================{}", trackingNum);
        String responseData = sendGetRequest(sendUrl.toString());
        log.info("============uniuni通过跟踪号跟踪货物响应========{}", responseData);
        return responseData;
    }

    private String getResponse(OkHttpClient client, Request request) {
        HttpUrl url = request.url();
        if (url.toString().contains(UniUniApiConstants.CREATE_BUSINESS_ORDER)
        ||url.toString().contains(UniUniApiConstants.CANCEL_ORDER)
        ||url.toString().contains(UniUniApiConstants.DELETE_ORDERS_BY_IAN)
        ) {
            // 不重试
            try {
                Response response = client.newCall(request).execute();
                String responseBody = "";
                if (response.isSuccessful()) {
                    // 处理返回的数据
                    if (ObjectUtil.isNotNull(response.body())) {
                        // response.body().string取一次后 再次取会异常 这里不反悔response直接返回 string
                        responseBody = response.body().string();
                    }
                } else {
                    log.info("未请求成功 响应码:{}", response.code());
                }
                return responseBody;
            } catch (IOException e) {
                // error里面输出错误详细信息 方便查找问题
                log.error("请求uni创建面单接口失败"+e.getMessage());
                Asserts.fail("接口请求失败");
            }
        } else {
            // 发送请求并获取响应
            Retryer build = uniRetryBuilder.build();
            Callable<String> getTokeCall = ()->{
                Response response = client.newCall(request).execute();
                String responseBody = "";
                if (response.isSuccessful()) {
                    // 处理返回的数据
                    if (ObjectUtil.isNotNull(response.body())) {
                        // response.body().string取一次后 再次取会异常 这里不反悔response直接返回 string
                        responseBody = response.body().string();
                        if (!url.toString().contains(UniUniApiConstants.PRINT_LABEL)) {
                            UniResponse uniResponse = JSONObject.parseObject(responseBody, UniResponse.class);
                            log.info("response body -> uniResponse:[{}]", uniResponse);
                            if (uniResponse.getErrCode().equals(20009)) {
                                // 没有token token过期 进行重试, 拿到token放入redis, 接口从redis中取token
                                getUniRedisToken();
                                // token 过期或失效后 拿到新的token 抛出需要重试异常 进行请求重试
                                throw new NeedRetryException(url + uniResponse.getRetMsg());
                            } else if (uniResponse.getErrCode().equals(20013)) {
                                // redis里的token非法 先删除redis里的token 再进行重试
                                // 先删除
                                stringRedisTemplate.delete(RedisKeyConstants.UNI_TOKEN);

                                getUniRedisToken();
                                // token 过期或失效后 拿到新的token 抛出需要重试异常 进行请求重试
                                throw new NeedRetryException(url + uniResponse.getRetMsg());
                            }
                        }
                    }
                } else {
                    log.info("未请求成功 响应码:{}", response.code());
                }
                return responseBody;
            };
            String responseBody = null;
            try {
                responseBody = (String) build.call(getTokeCall);
            } catch (ExecutionException | RetryException e) {
                Asserts.fail("接口请求失败");
            }
            return responseBody;
        }
        return "";
    }

    private String getApiPrefix() {
        String[] activeProfiles = environment.getActiveProfiles();
        String activeProfile = "";
        if (ObjectUtil.isNotEmpty(activeProfiles)) {
            activeProfile = environment.getActiveProfiles()[0];
        }
        String requestUrl = null;
        if (activeProfile.equals(EnvironmentConstants.PROD)) {
            requestUrl = prodUrl;
        } else {
            // 其他环境 用沙盒 沙盒不可用
            //requestUrl = sandboxUrl;
            // 用qa
            requestUrl = qaUrl;
        }
        log.info("uniuni当前环境:[{}], 请求地址前缀:[{}]", activeProfile, requestUrl);
        return requestUrl;
    }

    private String getUniRedisToken() {
        if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(RedisKeyConstants.UNI_TOKEN))) {
            // 有key
            return stringRedisTemplate.opsForValue().get(RedisKeyConstants.UNI_TOKEN);
        } else {
            // 无key第一次请求 或 token过期
            UniResponse.Data fullToken = getFullToken();
            UniResponse.Data tokenResponse = Optional.ofNullable(fullToken).orElse(new UniResponse.Data());
            String accessToken = Optional.ofNullable(tokenResponse.getAccessToken()).orElse("");
            String tokenType = Optional.ofNullable(tokenResponse.getTokenType()).orElse("");
            String firstCharUpper = tokenType.substring(0, 1).toUpperCase() + tokenType.substring(1);
            Long expiresIn = Optional.ofNullable(tokenResponse.getExpiresIn()).orElse(0L);
            StringBuilder fullAuth = new StringBuilder(firstCharUpper);
            fullAuth.append(" ");
            fullAuth.append(accessToken);
            String fullAuthVal = fullAuth.toString();
            if (StrUtil.isNotBlank(fullAuthVal)) {
                //存入redis缓存
                stringRedisTemplate.opsForValue().set(RedisKeyConstants.UNI_TOKEN, fullAuthVal, expiresIn, TimeUnit.SECONDS);
            }
            log.info("redis中无uni的toke, 发起获取token请求后拿到的token:{}", fullAuthVal);
            return fullAuthVal;
        }

    }
}
